//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
/// @file m5d_camera.h
///
/// Camera class.
///
//==================================================================

#ifndef M5D_CAMERA_H
#define M5D_CAMERA_H

#include "m5d_types.h"
#include "m5d_model.h"

//==================================================================
namespace M5D {

//==================================================================
class Camera : public Model
{
public:
	enum Type
	{
		CAMERA_TYPE_ORTHOGONAL,
		CAMERA_TYPE_PERSPECTIVE,
		LIGHT_TYPE_N,
	};

public:
	Type			_type;
	PMath::Matrix44	_projection_m;
	PMath::Matrix44	_cur_proj_world_m;
	PMath::Matrix44	_cur_cam_world_m;
	PMath::Matrix44	_cur_cam_world_33_m;
	int				_viewport[4];
	float			_viewport_minmax_z[2];

private:
	//--------------------------------------------------------------
	Camera( class Scene *scenep, const char *namep=NULL, Camera *copy_fromp=NULL );

public:
	~Camera();

	void SetType( Type type )
	{
		_type = type;
	}

	void SetProjMatrix( const PMath::Matrix44 &proj_m )
	{
		_projection_m = proj_m;
	}

	const PMath::Matrix44 &GetProjMatrix() const
	{
		return _projection_m;
	}

	inline PMath::Vector4 CalcWinSpace( const PMath::Vector4 &ws_pos )
	{
		PMath::Vector4	outvec;
		outvec = _cur_proj_world_m * ws_pos;
		float	oow = 1.0f / outvec._w;
		outvec *= oow;
		outvec._x = ((outvec._x+1) * _viewport[2]) * 0.5f + _viewport[0];
		outvec._y = ((1-outvec._y) * _viewport[3]) * 0.5f + _viewport[1];
		return outvec;
	}

	inline PMath::Vector4 CalcWinSpace( const PMath::Vector3 &ws_pos3 )
	{
		return CalcWinSpace( PMath::Vector4( ws_pos3, 1 ) );
	}

	void SetViewport( int x, int y, int w, int h, float min_z=0, float max_z=1 );
	void SetViewport( const int xywh[4], float *minmax_zp=NULL )
	{
		if ( minmax_zp )
		{
			PASSERT( minmax_zp[0] >= 0 && minmax_zp[1] <= 1 );
			SetViewport( xywh[0], xywh[1], xywh[2], xywh[3], minmax_zp[0], minmax_zp[1] );
		}
		else
			SetViewport( xywh[0], xywh[1], xywh[2], xywh[3] );
	}

	void GetViewport( int out_xywh[4], float *out_minmax_zp=NULL ) const
	{
		out_xywh[0] = _viewport[0];
		out_xywh[1] = _viewport[1];
		out_xywh[2] = _viewport[2];
		out_xywh[3] = _viewport[3];

		if ( out_minmax_zp )
		{
			out_minmax_zp[0] = _viewport_minmax_z[0];
			out_minmax_zp[1] = _viewport_minmax_z[1];
		}
	}

	void SetFrustum( float l, float r, float b, float t, float n, float f );
	void SetPerspectiveProj( float fovy, float aspect, float n, float f );
	void SetOrthogonalProj( float x1, float x2, float y2, float y1, float n, float f );

	/** Calculate the world-space point of intersection between a plane and an
		half-ray starting from window coordinates.

		@param window_sp_x		-	Window-space X coordinate (e.g. mouse_x)
		@param window_sp_y		-	Window-space Y coordinate (e.g. mouse_y)
		@param plane			-	Equation of the plane to intersect.
		@param does_intersectp	-	Optional boolean feedback to see if the intersection
									was possible.
	*/
	PMath::Vector3	GetPlaneInterersFromWindow( float window_sp_x, float window_sp_y,
										const PMath::Vector4 &plane,
										bool *does_intersectp=NULL );

	void UpdateGlobalTransform();

private:
	void updateWinWorldMatrix();

	friend class	Model;
	friend class	Scene;
};

//==================================================================

};

#endif